package org.paradigmshift.timelapse.rest

import java.net.URI

import scala.io.Source

import org.apache.http.client.methods.HttpDelete
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase
import org.apache.http.client.methods.HttpGet
import org.apache.http.client.methods.HttpPost
import org.apache.http.client.methods.HttpPut
import org.apache.http.client.methods.HttpRequestBase
import org.apache.http.client.utils.URIBuilder
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.DefaultHttpClient
import org.paradigmshift.timelapse.actor.messages.Exit
import org.paradigmshift.timelapse.exceptions.AuthorizationException
import org.paradigmshift.timelapse.exceptions.DoesNotExistException
import org.paradigmshift.timelapse.exceptions.InvalidInputException
import org.paradigmshift.timelapse.exceptions.PermissionDeniedException
import org.paradigmshift.timelapse.exceptions.TimeLapseException

object JsonRestConnection {
  
  private val basePath = "/jira/rest/api/latest"
    
  private val httpClient = new DefaultHttpClient()
  
  private val serverUriActor = new ServerUriActor
  private val authActor = new AuthorizationActor
  
  serverUriActor.start()
  authActor.start()
  
  private def buildURI( path: String, parameters: Option[Map[String,String]] ): Option[URI] = {
    serverUriActor !? GetUrl match {
      case Some( uri:URI ) => {
        val uriBuilder = new URIBuilder( uri )
        uriBuilder.setPath( basePath + path )
	    parameters.getOrElse( Map() ) foreach { p => uriBuilder.addParameter( p._1, p._2 ) }
        Option( uriBuilder.build() )
      }
      case _ => None
    }
  }
    
  private def execute( request: HttpRequestBase, path: String, parameters: Option[Map[String, String]] = None ): Option[String] = {
    for { uri <- buildURI( path, parameters ) } yield request.setURI( uri )
    authActor !? GetAuth match {
      case Some( token:String ) => request.addHeader( "Authorization", token )
      case _ =>
    }
    
    val response = httpClient.execute( request )
    val data = Option( Source.fromInputStream( response.getEntity().getContent() ).mkString )
    
    response.getStatusLine.getStatusCode match {
      case i:Int if ( i >= 200 && i < 300 ) => data
      case 400 => throw new InvalidInputException( response.getStatusLine.getReasonPhrase )
      case 401 => throw new AuthorizationException( response.getStatusLine.getReasonPhrase )
      case 403 => throw new PermissionDeniedException( response.getStatusLine.getReasonPhrase )
      case 404 => throw new DoesNotExistException( response.getStatusLine.getReasonPhrase )
      case _ => throw new TimeLapseException( response.getStatusLine.getReasonPhrase )
    }
  }
  
  private def execute( request: HttpEntityEnclosingRequestBase, path: String, json: String ): Option[String] = {
    val stringEntity = new StringEntity( json )
    stringEntity.setContentType( "application/json" )
    request.setEntity( stringEntity )
    execute( request, path, None )
  }
  
  def setConnectionInfo( scheme: String, host: String, port: Int ) = serverUriActor ! new SetUrl( scheme, host, port )
  
  def setAuth( username:String, password:String ) = authActor ! SetAuth( username, password )
  def isAuthSet = (authActor !? GetAuth) != None
  def clearAuth = authActor ! ClearAuth
  
  def get( path: String, parameters: Map[String,String] ) = {
    val request = new HttpGet()
    request.addHeader( "accept", "application/json" )
    execute( request, path, Option( parameters ) )
  }
  def post( path: String, json: String ) = execute( new HttpPost, path, json )
  def put( path: String, json: String ) = execute( new HttpPut, path, json )
  def delete( path: String ) = execute( new HttpDelete(), path )
  
  def close() {
    httpClient.getConnectionManager().shutdown()
    serverUriActor ! Exit
    authActor ! Exit
  }
}